package polsl.biai.projekt.server.calculation;


/**
 * all main calculations are done here
 * 
 * @author jacek
 *
 */
public class PopulationManager {

	/**
	 * used to mutate Cities
	 * not all Cities should be mutated, just few
	 */
    private static final double mutationRate = 0.015;
    
    /**
     * used to select random Tour from Population
     */
    private static final int selectionSize = 5;


    /**
     * makes new Population from the previous one by evolving it
     * @param pop old Population
     * @return new, evolved Population
     */
    public static Population evolvePopulation(Population pop) {
    	
    	//create new Population, don't initialize it
        Population newPopulation = new Population(pop.populationSize(), false);

        //get the shortest tour from old Population
        //and add it to new Population
        newPopulation.addTour(0, pop.getShortest());

        //crossover a whole Population 
        //and add new crossovered Tours to new Population
        for (int i = 1; i < newPopulation.populationSize(); i++) {
            //get parents
            Tour parent1 = selectTour(pop);
            Tour parent2 = selectTour(pop);
            //crossover parents
            Tour child = crossover(parent1, parent2);
            //add child to new population
            newPopulation.addTour(i, child);
        }
        
        //mutate the new Population
        for (int i = 1; i < newPopulation.populationSize(); i++) {
        	
        	//mutate every Tour
            mutate(newPopulation.getTour(i));
        }

        return newPopulation;
    }

    /**
     * crossovers two tours
     * @param parent1
     * @param parent2
     * @return
     */
    public static Tour crossover(Tour parent1, Tour parent2) {
        Tour child = new Tour();

        //generate randomly start and end pos 
        int startPos = (int) (Math.random() * parent1.tourSize());
        int endPos = (int) (Math.random() * parent1.tourSize());

       //crossover two Tours from startPos to endPos
        for (int i = 0; i < child.tourSize(); i++) {
            //check if startPos < endPos and 'i' between these points
        	//if yes adds City from parent1 to the child
            if (startPos < endPos && i > startPos && i < endPos) {
                child.addCity(i, parent1.getCity(i));
            } 
            //does the same operation like above but for
            //startPos > endPos
            else if (startPos > endPos) {
                if (!(i < startPos && i > endPos)) {
                    child.addCity(i, parent1.getCity(i));
                }
            }
        }

        //adds Cities from parent2 to the child Tour
        for (int i = 0; i < parent2.tourSize(); i++) {
            //checks if child contains current City from parent2
            if (!child.containsCity(parent2.getCity(i))) {
                //if not, add current City to child on 1st free position 
                for (int ii = 0; ii < child.tourSize(); ii++) {                    
                    if (child.getCity(ii) == null) {
                        child.addCity(ii, parent2.getCity(i));
                        break;
                    }
                }
            }
        }
        return child;
    }

    /**
     * mutate the Tour
     * @param tour Tour to mutate
     */
    private static void mutate(Tour tour) {
        
        for(int tourPos1=0; tourPos1 < tour.tourSize(); tourPos1++){
            //don't mutate all cities, just few
            if(Math.random() < mutationRate){
                //get a 2nd index of City to mutate
                int tourPos2 = (int) (tour.tourSize() * Math.random());

                //get two Cities
                City city1 = tour.getCity(tourPos1);
                City city2 = tour.getCity(tourPos2);

                //exchange their positions
                tour.addCity(tourPos2, city1);
                tour.addCity(tourPos1, city2);
            }
        }
    }

    /**
     * randomly select a Tour from Population
     * @param pop Population
     * @return the shortest Tour from Population
     */
    private static Tour selectTour(Population pop) {
    	
        //create new temporary Population
        Population tournament = new Population(selectionSize, false);
        
        //fill it with random Tours on random positions
        for (int i = 0; i < selectionSize; i++) {
            int randomId = (int) (Math.random() * pop.populationSize());
            tournament.addTour(i, pop.getTour(randomId));
        }
        //get the shortest Tour from this Population
        Tour shortest = tournament.getShortest();
        return shortest;
    }
}
